<?php


namespace app\common;

use app\Exception\UserException;
use app\utils\redis\RedisCache;
use app\utils\redis\RedisObj;
use Firebase\JWT\JWT;
use think\facade\Cache;
use think\facade\Env;
use think\Request;

class RefreshToken
{
    /**
     * 设置RefreshToken
     * @param string $email 邮箱
     * @param int $user_id 用户id
     * @return string
     */
    static public function setRefreshToken(string $email, int $user_id):string {
        return self::setRefreshTokenByCache($email, $user_id);
    }

    /**
     * 删除旧的token
     * @param string $key
     */
    static private function removeOldToken(string $key, bool $isRedis=true){
        if($isRedis){
            RedisCache::dDeleteKey(RedisObj::TOKEN_DB, $key);
        }else{
            Cache::delete($key);
        }
    }

    /** 生成key
     * @param string $id
     * @param string $email
     * @return string
     */
    static public function buildKey(string $id, string $email){
        return "refresh_{$id}_{$email}";
    }

    // 通过Cache保存token在本地
    static private function setRefreshTokenByCache(string $email, int $user_id, bool $isRedis = true): string{
        $nowTime = time();
        $payload = array(
            "email" => $email,
            "user_id" => $user_id,
            "start_time" => $nowTime,
            "end_time" => $nowTime + Env::get('jwt.refresh_timestamp'),
        );
        $key = self::buildKey($payload['user_id'], $payload['email']);
        self::removeOldToken($key);
        $jwtKey = Env::get('jwt.key');
        $timesecond = Env::get('jwt.refresh_timesecond', 1296000);
        $jwtToken = JWT::encode($payload, $jwtKey);
        if($isRedis){
            RedisCache::wSetValue(RedisObj::TOKEN_DB, $key, $jwtToken, $timesecond);
        }else{
            Cache::set($key, $jwtToken, $timesecond);
        }
        return $jwtToken;
    }

    /**
     * 通过RefreshToken获取Token
     * @param Request $request
     * @return string
     */
    static public function getJWTToken(Request $request){
        $refresh_token = $request->header('refresh-token');
        if(!$refresh_token) {
            throw new UserException(40004, '请填写refresh-token');
        }
        // 校验token
        $decoded = self::checkByCache($refresh_token);
        return Token::setToken($decoded['email'],$decoded['user_id']);
    }

    // 首先校验RefreshToken是否过期，过去则重新登录，没有过期的话则生成Token
    static private function checkByCache(string $refresh_token, bool $isRedis=true){
        try {
            $decoded = (array)JWT::decode($refresh_token, Env::get('jwt.key'), array('HS256'));
        }catch (\Exception $e) {
            throw new UserException(40004, 'RefreshToken无效，请重新登录');
        }
        $key = self::buildKey($decoded['user_id'], $decoded['email']);
        $re = null;
        if($isRedis){
            $re = RedisCache::rGetValue(RedisObj::TOKEN_DB, $key);
        }else{
            $re = Cache::get($key, false);
        }
        if($re != $refresh_token){
            throw new UserException(40004, 'RefreshToken无效，请重新登录');
        }
        // 校验成功后，则给RefreshToken续期
        $jwtKey = Env::get('jwt.key');
        $timesecond = Env::get('jwt.refresh_timesecond');
        // 这里的value => $jwtKey是没有实际意义的
        if($isRedis){
            RedisCache::wSetValue(RedisObj::TOKEN_DB,$decoded['user_id'] . "_refresh", $jwtKey, $timesecond);
        }else{
            Cache::set($decoded['user_id'] . "_refresh", $jwtKey, $timesecond);
        }
        return $decoded;
    }
}